<?php


namespace tmcore\services;

use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use PhpOffice\PhpSpreadsheet\Style\Alignment;
use PhpOffice\PhpSpreadsheet\Style\Border;
use tmcore\utils\Helper;
use tmcore\utils\File;

class ExportExcelService
{
    private static $instance = null;
    // 实例化 Spreadsheet
    private static Spreadsheet $spreadsheet;
    // 实例化 sheet
    private static Worksheet $sheet;
    // 导出文件唯一标识
    private static string $identifier = '';
    // 文件名称
    protected static string $fileName = '';
    // 文件后缀
    protected static string $fileSuffix = '';
    // 保存文件目录
    protected static string $path = 'data/download/export/';
    // 表头数据
    protected static array $header = [];
    // 写入数据总数
    protected static int $data_count = 0;
    // 写入数据进度
    protected static int $data_progress = 0;
    // 表头占列数
    protected static string $header_columns = '';
    // 表头占行数
    protected static int $header_rows = 3;
    // 设置样式
    protected static array $style = [
        'borders' => [
            'outline' => [
                'borderStyle' => Border::BORDER_THICK
            ],
        ],
        'alignment' => [
            'horizontal' => Alignment::HORIZONTAL_CENTER,
            'vertical' => Alignment::VERTICAL_CENTER
        ]
    ];

    // 初始化
    public function __construct()
    {
        self::$spreadsheet = new Spreadsheet();
        self::$sheet = self::$spreadsheet->getActiveSheet();
    }

    // 获取静态实例
    public static function instance()
    {
        if (self::$instance === null) self::$instance = new self();
        self::$fileSuffix = self::getFileSuffix();
        self::$identifier = md5(self::$fileSuffix);
        return self::$instance;
    }

    /**
     * 设置保存目录
     * @param $path
     * @return ExportExcelService
     */
    public function setPath($path)
    {
        self::$path = $path;
        return self::$instance;
    }

    /**
     * 设置文件标识
     * @param $value
     * @return ExportExcelService
     */
    public function setFileSuffix($value)
    {
        if($value)self::$fileSuffix = $value;
        return self::$instance;
    }

    /**
     * 获取excel保存路径
     * @param string $fileName 文件名称
     * @param bool $is_full 是否返回全路径
     * @return string
     */
    public static function getFileSavePath($fileName, $is_full = true)
    {
        $root_path = app()->getRootPath();
        $directory_path = self::$path . date('Ym') . '/' . date('d');
        if (!is_dir($root_path . $directory_path)) {
            if (File::mk_dir($root_path . $directory_path, 0755, true) == false) {
                return false;
            }
        }

        return ($is_full?$root_path:'') . $directory_path . '/' . $fileName;
    }

    /**
     * 获取导出文件标识
     * @return string
     */
    public function getIdentifier()
    {
        return self::$identifier;
    }

    /**
     * 获取文件名称
     * @return string
     */
    public function getFileName()
    {
        return self::$fileName;
    }

    /**
     * 获取写入进度
     * @return string
     */
    public function getProgress()
    {
        return self::$data_progress;
    }

    /**
     * 获取文件标识
     * @return string
     */
    public static function getFileSuffix()
    {
        return date('YmdHis') . '_' . Helper::get_random(6, '0123456789');
    }

    /**
     * @param string $title 文件标题
     * @param array|string $explain 说明内容 ['name'=>'','time'=>'','mobile'=> ''] | 自定义内容
     * @return ExportExcelService
     */
    public function setTitle($title, $explain = null)
    {
        self::$spreadsheet->getProperties()
            ->setTitle($title)
            ->setSubject($title);

        // 设置标题 居中 合并单元格 设置样式
        self::$sheet->setCellValue('A1', $title);
        self::$sheet->getStyle('A1')->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);
        self::$sheet->mergeCells('A1:' . self::$header_columns . '1');
        self::$sheet->getRowDimension(1)->setRowHeight(30);
        self::$sheet->getStyle('A1')->getFont()->setName('黑体');
        self::$sheet->getStyle('A1')->getFont()->setSize(16);

        if($explain) {
            $content = '';
            if(is_array($explain)) {
                $sheet_data = [];
                $sheet_data[0] = '操作人：' . (empty($explain['name'])? '':$explain['name']);
                $sheet_data[1] = '导出时间：' . (empty($explain['time'])? date('Y-m-d', time()):$explain['time']);
                if(!empty($explain['mobile'])) $sheet_data[2] = '手机号：' . $explain['mobile'];
                $content = implode(' ', $sheet_data);
            } else if (is_string($explain)) {
                $content = $explain;
            }

            // 设置内容 居中 合并单元格 设置样式
            self::$sheet->setCellValue('A2', $content);
            self::$sheet->getStyle('A2')->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);
            self::$sheet->mergeCells('A2:' . self::$header_columns . '2');
            self::$sheet->getRowDimension(2)->setRowHeight(20);
            self::$sheet->getStyle('A2')->getFont()->setName('宋体');
            self::$sheet->getStyle('A2')->getFont()->setSize(14);
            self::$sheet->getRowDimension(3)->setRowHeight(20);
        }

        return self::$instance;
    }

    /**
     * 设置表头
     * @param $header
     * @param $header_rows
     * @return ExportExcelService
     */
    public function setHeader($header, $header_rows = 0)
    {
        $column = 'A';
        $i = count($header);
        if($header_rows) self::$header_rows = $header_rows;
        foreach ($header as $key => $value) {
            self::$sheet->getColumnDimension($column);
            self::$sheet->setCellValue($column . self::$header_rows, $value);
            $i--;
            if($i>0)$column++;
        }
        self::$header_columns = $column;
        self::$header = $header;
        return self::$instance;
    }

    /**
     * 根据header获取数据
     * @param $data
     * @param $header
     * @return array
     */
    protected function getDataByHeader($data, $header)
    {
        $arr = [];
        foreach ($header as $key => $value) {
            array_push($arr, $data[$key]);
        }
        return $arr;
    }

    /**
     * 设置数据
     * @param array $data
     * @param callable $callFn 进度回调
     * @param bool $is_limit
     * @return ExportExcelService
     */
    public function setData($data, $callFn = null, $is_limit = true)
    {
        $column = '';
        $row = self::$header_rows + 1;
        $progress = 0;
        self::$data_count = count($data);
        $progress_limit = ceil(self::$data_count/100);
        $progress_count = 0;
        $progress_success = false;

        // 行循环
        foreach ($data as $key => $value) {
            $column = 'A';
            $value = $this->getDataByHeader($value, self::$header);
            $i = count($value);
            // 列循环
            foreach ($value as $k => $v) {
                self::$sheet->setCellValue($column . $row, $v);
                $i --;
                if($i>0)$column++;
            }
            $row++;
            $progress++;
            // 写入导入进度
            self::$data_progress = intval($progress/self::$data_count * 100);

            // 限制进度颗粒度，防止频繁回调写入
            if((($progress - $progress_count >= $progress_limit) || self::$data_progress == 100) && $is_limit && $progress_success == false) {
                $progress_count = $progress;
                if(self::$data_progress == 100) $progress_success = true;
                // 回调传参导出进度
                if($callFn && is_callable($callFn)) {
                    call_user_func($callFn,self::$data_progress, $progress, self::$data_count);
                }
            }
        }
        $row--;

        if(self::$data_count) {
            //设置 字体样式 边框 换行
            self::$sheet->getStyle('A1:' . $column . $row)->applyFromArray(self::$style);
            self::$sheet->getStyle('A1:' . $column . $row)->getBorders()->getAllBorders()->setBorderStyle(Border::BORDER_THIN);
            self::$sheet->getStyle('A4:' . $column . $row)->getAlignment()->setWrapText(true);
        }

        return self::$instance;
    }

    /**
     * 创建excel
     * @param string $fileName 文件名称
     * @param string $suffix 文件名称
     * @param string $save_path 保存路径
     * @return bool|string
     */
    public function createExcel($fileName = '', $suffix = 'xlsx', $save_path = '')
    {
        if (!$fileName) return false;
        if (!$suffix) $suffix = 'xlsx';

        if ($suffix == 'xlsx') {
            header("Content-Type:application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        } else {
            $suffix = 'xls';
            header("Content-Type:application/vnd.ms-excel");
        }

        $spreadsheet = self::$spreadsheet;

        // 调用方法执行下载
        $writer = IOFactory::createWriter($spreadsheet, ucwords($suffix));

        $fileName = $fileName . '.' . $suffix;
        self::$fileName = $fileName;

        if($save_path) {
            $path = $save_path . '/' . $fileName;
            // 数据流
            $writer->save($path);

            // 删除清空 释放内存
            $spreadsheet->disconnectWorksheets();
            unset($spreadsheet);
            return self::getFileSavePath($fileName, false);
        } else {
            // 激活浏览器窗口
            header("Content-Disposition:attachment;filename=" . $fileName);
            // 缓存控制
            header("Cache-Control:max-age=0");

            // 数据流
            $writer->save('php://output');

            // 删除清空 释放内存
            $spreadsheet->disconnectWorksheets();
            unset($spreadsheet);
            exit;
        }
    }

    /**
     * 导出excel
     * @param string $fileName 文件名称
     * @param string $suffix 文件名称
     * @param bool $is_save 是否保存
     * @return string
     */
    public function exportExcel($fileName = '', $suffix = 'xlsx', $is_save = true)
    {
        if (!$fileName) $fileName = '导出文件';
        if (!$suffix) $suffix = 'xlsx';

        // 防止文件名重复
        $file_name_suffix = '_' . self::$fileSuffix;
        $fileName = $fileName . $file_name_suffix;

        if ($suffix == 'xlsx') {
            header("Content-Type:application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        } else {
            $suffix = 'xls';
            header("Content-Type:application/vnd.ms-excel");
        }

        $spreadsheet = self::$spreadsheet;

        // 调用方法执行下载
        $writer = IOFactory::createWriter($spreadsheet, ucwords($suffix));

        $fileName = $fileName . '.' . $suffix;
        self::$fileName = $fileName;

        if($is_save) {
            $path = self::getFileSavePath($fileName);
            // 数据流
            $writer->save($path);

            // 删除清空 释放内存
            $spreadsheet->disconnectWorksheets();
            unset($spreadsheet);
            return self::getFileSavePath($fileName, false);
        } else {
            // 激活浏览器窗口
            header("Content-Disposition:attachment;filename=" . $fileName);
            // 缓存控制
            header("Cache-Control:max-age=0");

            // 数据流
            $writer->save('php://output');

            // 删除清空 释放内存
            $spreadsheet->disconnectWorksheets();
            unset($spreadsheet);
            exit;
        }
    }
}